<template>
  <div class="ansible-playbooks">
    <div class="page-header">
      <h2>🏕️ Playbook编辑执行</h2>
      <p class="subtitle">在线编辑和执行Ansible Playbook</p>
    </div>

    <!-- 操作栏 -->
    <div class="action-bar">
      <div class="tabs">
        <button 
          class="tab-btn" 
          :class="{ active: currentTab === 'edit' }"
          @click="currentTab = 'edit'"
        >
          📝 在线编辑
        </button>
        <button 
          class="tab-btn" 
          :class="{ active: currentTab === 'upload' }"
          @click="currentTab = 'upload'"
        >
          📂 选择现有
        </button>
      </div>
      <button class="btn btn-primary" @click="showPlaybookList = true">
        📋 Playbook列表
      </button>
    </div>

    <!-- 主内容区 -->
    <div class="content-container">
      <!-- 左侧编辑器 -->
      <div class="editor-panel">
        <div class="panel-header">
          <div class="config-row">
            <label>Ansible配置:</label>
            <select v-model="selectedAnsibleConfig" class="select-input">
              <option value="">选择Ansible配置</option>
              <option v-for="inv in inventories" :key="inv.id" :value="inv.id">
                {{ inv.name }} - {{ inv.host_address }}
              </option>
            </select>
          </div>
          
          <div class="config-row">
            <label>目标主机:</label>
            <select v-model="selectedTargetHost" class="select-input">
              <option value="">选择目标主机</option>
              <option v-for="host in hosts" :key="host.id" :value="host.id">
                {{ host.name }} ({{ host.ip }})
              </option>
            </select>
          </div>
          
          <div class="config-row">
            <label>主机清单:</label>
            <select v-model="selectedInventory" class="select-input">
              <option value="">选择主机清单</option>
              <option v-for="inv in inventories" :key="inv.id" :value="inv.id">
                {{ inv.name }} ({{ inv.host_count }}台主机)
              </option>
            </select>
          </div>
          
          <div class="config-row">
            <label>执行凭据:</label>
            <select v-model="selectedCredential" class="select-input">
              <option value="">选择凭据</option>
              <option v-for="cred in credentials" :key="cred.id" :value="cred.id">
                {{ cred.name }} ({{ getCredTypeText(cred.credential_type) }})
              </option>
            </select>
          </div>
        </div>

        <div class="editor-toolbar">
          <input 
            v-model="playbookName" 
            placeholder="playbook.yml" 
            class="playbook-name-input"
          />
          <input 
            v-model="remotePath" 
            placeholder="目标路径 (如: /opt/playbooks)" 
            class="remote-path-input"
            title="Playbook将上传到目标主机的此路径"
          />
          <label class="btn btn-sm btn-upload">
            📁 本地选择
            <input type="file" accept=".yml,.yaml" @change="handleFileUpload" style="display: none" />
          </label>
          <button class="btn btn-sm btn-upload" @click="uploadToHost" :disabled="isUploading">
            {{ isUploading ? '⏳ 上传中...' : '☁️ 上传到主机' }}
          </button>
          <button class="btn btn-sm btn-info" @click="browseRemoteFiles" :disabled="isBrowsing">
            {{ isBrowsing ? '⏳ 加载中...' : '📂 浏览主机文件' }}
          </button>
          <button class="btn btn-sm btn-success" @click="executePlaybook" :disabled="isExecuting">
            {{ isExecuting ? '⏳ 执行中...' : '▶️ 执行' }}
          </button>
          <button class="btn btn-sm btn-info" @click="syntaxCheck">
            ✓ 语法检查
          </button>
          <button class="btn btn-sm" @click="savePlaybook">
            💾 保存
          </button>
          <button class="btn btn-sm" @click="downloadPlaybook">
            ⬇️ 下载
          </button>
        </div>

        <!-- Monaco编辑器 -->
        <div ref="editorContainer" class="monaco-editor-container"></div>
      </div>

      <!-- 右侧执行结果 -->
      <div class="result-panel">
        <div class="result-header">
          <span class="result-title">📊 执行结果</span>
          <div class="result-actions">
            <button class="btn btn-sm" @click="clearResult">🗑️ 清空</button>
            <button class="btn btn-sm btn-primary" @click="copyResult">📋 复制</button>
            <button class="btn btn-sm" @click="downloadResult">⬇️ 下载</button>
          </div>
        </div>
        
        <div class="result-content" ref="resultContent">
          <div v-if="!currentResult" class="result-empty">
            <div class="empty-icon">🏕️</div>
            <p>等待执行Playbook...</p>
            <small>点击"执行"按钮开始运行</small>
          </div>
          
          <div v-else class="result-output">
            <div v-if="currentResult.status === 'running'" class="status-badge status-running">
              ⏳ 运行中...
            </div>
            <div v-else-if="currentResult.status === 'success'" class="status-badge status-success">
              ✅ 执行成功
            </div>
            <div v-else-if="currentResult.status === 'failed'" class="status-badge status-failed">
              ❌ 执行失败
            </div>
            
            <div class="result-stats" v-if="currentResult.result">
              <div class="stat-item">
                <span class="stat-label">耗时:</span>
                <span class="stat-value">{{ currentResult.duration || 'N/A' }}</span>
              </div>
              <div class="stat-item">
                <span class="stat-label">主机:</span>
                <span class="stat-value">{{ currentResult.inventory_name }}</span>
              </div>
            </div>
            
            <div class="output-section">
              <h4>📤 标准输出</h4>
              <pre class="output-text">{{ currentResult.stdout || '无输出' }}</pre>
            </div>
            
            <div v-if="currentResult.stderr" class="output-section error">
              <h4>⚠️ 错误输出</h4>
              <pre class="output-text">{{ currentResult.stderr }}</pre>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Playbook列表弹窗 -->
    <div v-if="showPlaybookList" class="modal-overlay" @click="showPlaybookList = false">
      <div class="modal-content" @click.stop>
        <div class="modal-header">
          <h3>📋 Playbook列表</h3>
          <button class="btn-close" @click="showPlaybookList = false">✕</button>
        </div>
        <div class="modal-body">
          <div class="playbook-list">
            <div 
              v-for="pb in playbooks" 
              :key="pb.id" 
              class="playbook-item"
              @click="loadPlaybook(pb)"
            >
              <div class="playbook-info">
                <div class="playbook-title">{{ pb.name }}</div>
                <div class="playbook-meta">
                  创建者: {{ pb.created_by_name }} | {{ formatDate(pb.created_at) }}
                </div>
              </div>
              <div class="playbook-actions">
                <button class="btn btn-sm btn-danger" @click.stop="deletePlaybook(pb.id)">
                  删除
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 文件浏览器模态框 -->
    <div v-if="showFileBrowser" class="modal-overlay" @click.self="showFileBrowser = false">
      <div class="file-browser-modal">
        <div class="browser-header">
          <div class="header-content">
            <span class="header-icon">📁</span>
            <h3>浏览远程主机文件</h3>
          </div>
          <button class="close-btn" @click="showFileBrowser = false">
            <span>✕</span>
          </button>
        </div>

        <div class="browser-path-bar">
          <div class="path-display">
            <span class="path-label">当前路径</span>
            <div class="breadcrumb">
              <span class="breadcrumb-item root" @click="navigateToDirectory('/')" title="根目录">
                <span class="icon">🏠</span>
                <span class="text">root</span>
              </span>
              <template v-for="(part, index) in pathParts" :key="index">
                <span class="breadcrumb-separator">›</span>
                <span 
                  class="breadcrumb-item" 
                  @click="navigateToPathIndex(index)"
                  :title="part"
                >
                  {{ part }}
                </span>
              </template>
            </div>
          </div>
          <div class="path-actions">
            <button 
              class="action-btn" 
              @click="navigateUp" 
              :disabled="currentBrowsePath === '/'"
              title="返回上级目录"
            >
              <span class="icon">⬆️</span>
              <span class="label">上级</span>
            </button>
            <button 
              class="action-btn refresh" 
              @click="refreshBrowser"
              title="刷新当前目录"
            >
              <span class="icon">🔄</span>
              <span class="label">刷新</span>
            </button>
          </div>
        </div>

        <div class="browser-content">
          <div v-if="isLoadingBrowser" class="browser-loading">
            <div class="loading-spinner">⏳</div>
            <p>正在加载目录内容...</p>
          </div>

          <div v-else-if="browserError" class="browser-error">
            <div class="error-icon">❌</div>
            <p class="error-message">{{ browserError }}</p>
            <button class="retry-btn" @click="refreshBrowser">
              <span>🔄</span>
              <span>重新加载</span>
            </button>
          </div>

          <div v-else class="browser-list">
            <!-- 目录列表 -->
            <div v-if="browserDirectories.length > 0" class="item-section">
              <div class="section-header">
                <span class="section-icon">📂</span>
                <span class="section-title">目录</span>
                <span class="section-count">{{ browserDirectories.length }}</span>
              </div>
              <div class="items-grid">
                <div
                  v-for="dir in browserDirectories"
                  :key="dir.path"
                  class="browser-item directory-item"
                  @click="navigateToDirectory(dir.path)"
                >
                  <div class="item-icon-wrapper">
                    <span class="item-icon">📁</span>
                  </div>
                  <div class="item-info">
                    <span class="item-name">{{ dir.name }}</span>
                  </div>
                  <span class="item-arrow">›</span>
                </div>
              </div>
            </div>

            <!-- 文件列表 -->
            <div v-if="browserFiles.length > 0" class="item-section">
              <div class="section-header">
                <span class="section-icon">📄</span>
                <span class="section-title">YAML文件</span>
                <span class="section-count">{{ browserFiles.length }}</span>
              </div>
              <div class="items-grid">
                <div
                  v-for="file in browserFiles"
                  :key="file.path"
                  class="browser-item file-item"
                  @click="selectRemoteFile(file)"
                >
                  <div class="item-icon-wrapper">
                    <span class="item-icon">📝</span>
                  </div>
                  <div class="item-info">
                    <span class="item-name">{{ file.name }}</span>
                    <span class="item-size">{{ formatSize(file.size) }}</span>
                  </div>
                </div>
              </div>
            </div>

            <!-- 空状态 -->
            <div v-if="browserDirectories.length === 0 && browserFiles.length === 0" class="browser-empty">
              <div class="empty-icon">📭</div>
              <p class="empty-title">目录为空</p>
              <p class="empty-subtitle">该目录下没有YAML文件和子目录</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import * as monaco from 'monaco-editor'
import api from '../services/api'

// 数据
const currentTab = ref('edit')
const playbookName = ref('playbook.yml')
const remotePath = ref('/root')
const playbookContent = ref(`---
- name: 示例Playbook
  hosts: all
  become: yes
  
  vars:
    app_name: "my-app"
  
  tasks:
    - name: 测试连接
      ping:
    
    - name: 输出信息
      debug:
        msg: "Hello from {{ inventory_hostname }}"
`)

const hosts = ref([])
const inventories = ref([])
const credentials = ref([])
const playbooks = ref([])
const selectedAnsibleConfig = ref('')
const selectedTargetHost = ref('')
const selectedInventory = ref('')
const selectedCredential = ref('')
const currentResult = ref(null)
const isExecuting = ref(false)
const isUploading = ref(false)
const isBrowsing = ref(false)
const showPlaybookList = ref(false)
const remoteFiles = ref([])

// 文件浏览器
const showFileBrowser = ref(false)
const currentBrowsePath = ref('/root')
const browserDirectories = ref([])
const browserFiles = ref([])
const isLoadingBrowser = ref(false)
const browserError = ref('')

// Monaco编辑器
const editorContainer = ref(null)
let editor = null

// 计算属性
const pathParts = computed(() => {
  if (!currentBrowsePath.value || currentBrowsePath.value === '/') return []
  return currentBrowsePath.value.split('/').filter(p => p)
})

// 初始化Monaco编辑器
const initMonacoEditor = () => {
  if (!editorContainer.value) return

  // 注册YAML语言
  monaco.languages.register({ id: 'yaml' })

  // Ansible模块补全数据
  const ansibleModules = [
    // 系统模块
    { label: 'ping', kind: monaco.languages.CompletionItemKind.Module, insertText: 'ping:', documentation: '测试与主机的连接' },
    { label: 'setup', kind: monaco.languages.CompletionItemKind.Module, insertText: 'setup:', documentation: '收集主机facts信息' },
    { label: 'service', kind: monaco.languages.CompletionItemKind.Module, insertText: 'service:\n  name: ${1:service_name}\n  state: ${2|started,stopped,restarted,reloaded|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '管理系统服务' },
    { label: 'systemd', kind: monaco.languages.CompletionItemKind.Module, insertText: 'systemd:\n  name: ${1:service}\n  state: ${2|started,stopped,restarted,reloaded|}\n  enabled: ${3|yes,no|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '管理systemd服务' },
    { label: 'user', kind: monaco.languages.CompletionItemKind.Module, insertText: 'user:\n  name: ${1:username}\n  state: ${2|present,absent|}\n  shell: ${3:/bin/bash}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '管理用户账户' },
    { label: 'group', kind: monaco.languages.CompletionItemKind.Module, insertText: 'group:\n  name: ${1:groupname}\n  state: ${2|present,absent|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '管理用户组' },
    
    // 文件模块
    { label: 'copy', kind: monaco.languages.CompletionItemKind.Module, insertText: 'copy:\n  src: ${1:source}\n  dest: ${2:destination}\n  owner: ${3:root}\n  mode: ${4:\'0644\'}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '复制文件到远程主机' },
    { label: 'file', kind: monaco.languages.CompletionItemKind.Module, insertText: 'file:\n  path: ${1:path}\n  state: ${2|file,directory,absent,touch|}\n  mode: ${3:\'0755\'}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '管理文件和目录' },
    { label: 'template', kind: monaco.languages.CompletionItemKind.Module, insertText: 'template:\n  src: ${1:template.j2}\n  dest: ${2:destination}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '使用Jinja2模板生成文件' },
    { label: 'lineinfile', kind: monaco.languages.CompletionItemKind.Module, insertText: 'lineinfile:\n  path: ${1:filepath}\n  line: ${2:\'text\'}\n  state: ${3|present,absent|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '管理文件中的行' },
    { label: 'blockinfile', kind: monaco.languages.CompletionItemKind.Module, insertText: 'blockinfile:\n  path: ${1:filepath}\n  block: |\n    ${2:content}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '插入/更新/删除文件中的文本块' },
    
    // 命令模块
    { label: 'command', kind: monaco.languages.CompletionItemKind.Module, insertText: 'command: ${1:command}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '执行命令' },
    { label: 'shell', kind: monaco.languages.CompletionItemKind.Module, insertText: 'shell: ${1:command}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '通过shell执行命令' },
    { label: 'script', kind: monaco.languages.CompletionItemKind.Module, insertText: 'script: ${1:script.sh}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '执行脚本文件' },
    { label: 'raw', kind: monaco.languages.CompletionItemKind.Module, insertText: 'raw: ${1:command}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '执行原始命令' },
    
    // 包管理
    { label: 'yum', kind: monaco.languages.CompletionItemKind.Module, insertText: 'yum:\n  name: ${1:package}\n  state: ${2|present,absent,latest|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: 'YUM包管理' },
    { label: 'apt', kind: monaco.languages.CompletionItemKind.Module, insertText: 'apt:\n  name: ${1:package}\n  state: ${2|present,absent,latest|}\n  update_cache: yes', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: 'APT包管理' },
    { label: 'dnf', kind: monaco.languages.CompletionItemKind.Module, insertText: 'dnf:\n  name: ${1:package}\n  state: ${2|present,absent,latest|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: 'DNF包管理' },
    { label: 'pip', kind: monaco.languages.CompletionItemKind.Module, insertText: 'pip:\n  name: ${1:package}\n  state: ${2|present,absent,latest|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: 'Python pip包管理' },
    
    // Git
    { label: 'git', kind: monaco.languages.CompletionItemKind.Module, insertText: 'git:\n  repo: ${1:https://github.com/user/repo.git}\n  dest: ${2:/path/to/dest}\n  version: ${3:main}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: 'Git仓库管理' },
    
    // 调试和输出
    { label: 'debug', kind: monaco.languages.CompletionItemKind.Module, insertText: 'debug:\n  msg: ${1:\'message\'}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '打印调试信息' },
    { label: 'pause', kind: monaco.languages.CompletionItemKind.Module, insertText: 'pause:\n  seconds: ${1:10}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '暂停执行' },
    { label: 'assert', kind: monaco.languages.CompletionItemKind.Module, insertText: 'assert:\n  that:\n    - ${1:condition}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '断言条件' },
    
    // 同步
    { label: 'synchronize', kind: monaco.languages.CompletionItemKind.Module, insertText: 'synchronize:\n  src: ${1:source}\n  dest: ${2:destination}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '使用rsync同步文件' },
    { label: 'fetch', kind: monaco.languages.CompletionItemKind.Module, insertText: 'fetch:\n  src: ${1:remote_file}\n  dest: ${2:local_path}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '从远程主机获取文件' },
    
    // 其他常用模块
    { label: 'cron', kind: monaco.languages.CompletionItemKind.Module, insertText: 'cron:\n  name: ${1:job_name}\n  minute: ${2:\'0\'}\n  job: ${3:command}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '管理cron任务' },
    { label: 'mount', kind: monaco.languages.CompletionItemKind.Module, insertText: 'mount:\n  path: ${1:/mnt/point}\n  src: ${2:device}\n  fstype: ${3:ext4}\n  state: ${4|mounted,unmounted,present,absent|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '管理挂载点' },
    { label: 'get_url', kind: monaco.languages.CompletionItemKind.Module, insertText: 'get_url:\n  url: ${1:http://example.com/file}\n  dest: ${2:/path/to/dest}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: '从URL下载文件' },
    { label: 'uri', kind: monaco.languages.CompletionItemKind.Module, insertText: 'uri:\n  url: ${1:http://example.com}\n  method: ${2|GET,POST,PUT,DELETE|}', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, documentation: 'HTTP请求' },
  ]

  // 注册自动补全提供者
  monaco.languages.registerCompletionItemProvider('yaml', {
    provideCompletionItems: (model, position) => {
      const suggestions = [...ansibleModules]
      return { suggestions }
    }
  })

  // 创建编辑器实例
  editor = monaco.editor.create(editorContainer.value, {
    value: playbookContent.value,
    language: 'yaml',
    theme: 'vs-dark',
    automaticLayout: true,
    fontSize: 14,
    lineNumbers: 'on',
    minimap: { enabled: true },
    scrollBeyondLastLine: false,
    wordWrap: 'on',
    tabSize: 2,
    insertSpaces: true,
    folding: true,
    renderWhitespace: 'boundary',
    bracketPairColorization: {
      enabled: true
    }
  })

  // 监听编辑器内容变化
  editor.onDidChangeModelContent(() => {
    playbookContent.value = editor.getValue()
  })
}

// 方法
const loadData = async () => {
  try {
    const [hostData, invData, credData, pbData] = await Promise.all([
      api.getHosts(),
      api.getInventories(),
      api.getCredentials(),
      api.getPlaybooks()
    ])
    hosts.value = Array.isArray(hostData) ? hostData : (hostData.results || [])
    inventories.value = Array.isArray(invData) ? invData : (invData.results || [])
    credentials.value = Array.isArray(credData) ? credData : (credData.results || [])
    playbooks.value = Array.isArray(pbData) ? pbData : (pbData.results || [])
    console.log('数据加载成功:', {
      主机数: hosts.value.length,
      清单数: inventories.value.length,
      凭据数: credentials.value.length,
      Playbook数: playbooks.value.length
    })
  } catch (error) {
    console.error('加载数据失败:', error)
    hosts.value = []
    inventories.value = []
    credentials.value = []
    playbooks.value = []
    alert('❌ 加载数据失败: ' + (error.response?.data?.error || error.message))
  }
}

const getCredTypeText = (type) => {
  const map = {
    'ssh_password': '🔐密码',
    'ssh_key': '🔑密钥',
    'vault': '🔒Vault'
  }
  return map[type] || type
}

const handleFileUpload = (event) => {
  const file = event.target.files[0]
  if (!file) return

  if (!file.name.endsWith('.yml') && !file.name.endsWith('.yaml')) {
    alert('请选择.yml或.yaml文件')
    return
  }

  const reader = new FileReader()
  reader.onload = (e) => {
    const content = e.target.result
    if (editor) {
      editor.setValue(content)
    } else {
      playbookContent.value = content
    }
    playbookName.value = file.name
    alert('✅ 文件加载成功！')
  }
  reader.onerror = () => {
    alert('❌ 文件读取失败')
  }
  reader.readAsText(file)
  event.target.value = ''
}

const syntaxCheck = async () => {
  const content = editor ? editor.getValue() : playbookContent.value
  
  if (!content || !content.trim()) {
    alert('❌ Playbook内容为空')
    return
  }
  
  try {
    const result = await api.syntaxCheck({ content })
    if (result.valid) {
      alert('✅ 语法检查通过！')
    } else {
      alert(`❌ 语法错误:\n${result.message}`)
    }
  } catch (error) {
    alert('语法检查失败: ' + (error.response?.data?.error || error.message))
  }
}

const savePlaybook = async () => {
  if (!playbookName.value || !playbookName.value.trim()) {
    alert('❌ 请输入Playbook名称')
    return
  }
  
  const content = editor ? editor.getValue() : playbookContent.value
  
  if (!content || !content.trim()) {
    alert('❌ Playbook内容不能为空')
    return
  }
  
  try {
    const name = playbookName.value.trim()
    
    // 检查是否存在同名Playbook
    const existingPlaybook = playbooks.value.find(p => p.name === name)
    
    const data = {
      name: name,
      content: content,
      inventory: selectedInventory.value || null,
      credential: selectedCredential.value || null,
      status: 'active'
    }
    
    if (existingPlaybook) {
      // 存在则更新
      await api.updatePlaybook(existingPlaybook.id, data)
      alert('✅ Playbook已更新！')
    } else {
      // 不存在则创建
      await api.createPlaybook(data)
      alert('✅ Playbook保存成功！')
    }
    
    loadData()
    
    // 询问是否同步到主机
    if (selectedTargetHost.value && remotePath.value) {
      if (confirm('💾 Playbook已保存到数据库\n\n是否同步到远程主机？\n\n主机: ' + hosts.value.find(h => h.id === selectedTargetHost.value)?.name + '\n路径: ' + remotePath.value)) {
        await syncToHost(content)
      }
    }
  } catch (error) {
    console.error('保存失败:', error)
    
    // 提取详细错误信息
    let errorMsg = ''
    if (error.response?.data) {
      const data = error.response.data
      // 处理字段验证错误
      if (data.name && Array.isArray(data.name)) {
        errorMsg = data.name[0]
      } else if (data.error) {
        errorMsg = data.error
      } else if (typeof data === 'string') {
        errorMsg = data
      } else {
        errorMsg = JSON.stringify(data)
      }
    } else {
      errorMsg = error.message
    }
    
    alert('❌ 保存失败: ' + errorMsg)
  }
}

const syncToHost = async (content) => {
  if (!selectedTargetHost.value || !remotePath.value) {
    alert('❌ 请先选择目标主机和路径')
    return
  }
  
  isUploading.value = true
  try {
    const result = await api.uploadPlaybookToHost({
      content: content,
      filename: playbookName.value,
      target_host: selectedTargetHost.value,
      remote_path: remotePath.value
    })
    alert(`✅ 同步成功！\n\n主机: ${result.host}\n路径: ${result.remote_path}`)
  } catch (error) {
    console.error('同步失败:', error)
    alert('❌ 同步到主机失败: ' + (error.response?.data?.error || error.message))
  } finally {
    isUploading.value = false
  }
}

const uploadToHost = async () => {
  if (!selectedTargetHost.value) {
    alert('❌ 请先选择目标主机')
    return
  }
  
  if (!remotePath.value) {
    alert('❌ 请输入目标路径')
    return
  }
  
  const content = editor ? editor.getValue() : playbookContent.value
  
  if (!content) {
    alert('❌ Playbook内容为空')
    return
  }
  
  await syncToHost(content)
}

const downloadPlaybook = () => {
  const content = editor ? editor.getValue() : playbookContent.value
  const blob = new Blob([content], { type: 'text/yaml' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = playbookName.value
  a.click()
  URL.revokeObjectURL(url)
}

const browseRemoteFiles = async () => {
  if (!selectedTargetHost.value) {
    alert('❌ 请先选择目标主机')
    return
  }
  
  // 打开文件浏览器
  currentBrowsePath.value = remotePath.value || '/root'
  showFileBrowser.value = true
  loadBrowserContent()
}

const loadBrowserContent = async () => {
  if (!selectedTargetHost.value) return
  
  isLoadingBrowser.value = true
  browserError.value = ''
  
  try {
    const result = await api.listRemoteFiles({
      target_host: selectedTargetHost.value,
      remote_path: currentBrowsePath.value
    })
    
    browserDirectories.value = result.directories || []
    browserFiles.value = result.files || []
  } catch (error) {
    console.error('加载目录内容失败:', error)
    browserError.value = error.response?.data?.error || error.message
  } finally {
    isLoadingBrowser.value = false
  }
}

const navigateToDirectory = (path) => {
  currentBrowsePath.value = path
  loadBrowserContent()
}

const navigateToPathIndex = (index) => {
  const parts = pathParts.value.slice(0, index + 1)
  currentBrowsePath.value = '/' + parts.join('/')
  loadBrowserContent()
}

const navigateUp = () => {
  if (currentBrowsePath.value === '/') return
  
  const parts = currentBrowsePath.value.split('/').filter(p => p)
  parts.pop()
  currentBrowsePath.value = parts.length > 0 ? '/' + parts.join('/') : '/'
  loadBrowserContent()
}

const refreshBrowser = () => {
  loadBrowserContent()
}

const selectRemoteFile = async (file) => {
  if (!confirm(`确定要加载文件 ${file.name} 吗？\n当前编辑器内容将被替换。`)) {
    return
  }
  
  isLoadingBrowser.value = true
  try {
    const result = await api.readRemoteFile({
      target_host: selectedTargetHost.value,
      file_path: file.path
    })
    
    if (editor) {
      editor.setValue(result.content)
    } else {
      playbookContent.value = result.content
    }
    playbookName.value = result.filename
    
    // 记住文件的原始路径，用于后续保存同步
    const pathParts = file.path.split('/')
    pathParts.pop() // 移除文件名
    remotePath.value = pathParts.join('/') || '/'
    
    showFileBrowser.value = false
    alert(`✅ 成功加载文件: ${result.filename}\n\n原始路径: ${file.path}`)
  } catch (error) {
    console.error('读取文件失败:', error)
    alert('❌ 读取文件失败: ' + (error.response?.data?.error || error.message))
  } finally {
    isLoadingBrowser.value = false
  }
}

const formatSize = (bytes) => {
  if (bytes < 1024) return bytes + ' B'
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
  return (bytes / (1024 * 1024)).toFixed(1) + ' MB'
}

const executePlaybook = async () => {
  const content = editor ? editor.getValue() : playbookContent.value
  
  if (!content || !content.trim()) {
    alert('❌ Playbook内容不能为空')
    return
  }
  
  if (!selectedInventory.value) {
    alert('❌ 请选择主机清单')
    return
  }
  
  if (!selectedCredential.value) {
    alert('❌ 请选择执行凭据')
    return
  }
  
  isExecuting.value = true
  currentResult.value = { status: 'running', stdout: '正在执行Playbook...', stderr: '' }
  
  try {
    const result = await api.executePlaybookContent({
      content: content,
      inventory: selectedInventory.value,
      credential: selectedCredential.value,
      target_host: selectedTargetHost.value
    })
    
    currentResult.value = {
      status: result.status || 'success',
      stdout: result.stdout || '',
      stderr: result.stderr || ''
    }
    
    // 执行结果已显示在界面上，不需要额外弹窗
    console.log('Playbook执行完成:', result.status)
  } catch (error) {
    console.error('执行失败:', error)
    const errorMsg = error.response?.data?.error || error.message
    currentResult.value = {
      status: 'failed',
      stdout: '',
      stderr: errorMsg
    }
    // 错误信息已显示在结果面板中，不需要弹窗
  } finally {
    isExecuting.value = false
  }
}

const loadPlaybook = (playbook) => {
  playbookName.value = playbook.name
  if (editor) {
    editor.setValue(playbook.content)
  } else {
    playbookContent.value = playbook.content
  }
  if (playbook.inventory) selectedInventory.value = playbook.inventory
  if (playbook.credential) selectedCredential.value = playbook.credential
  showPlaybookList.value = false
}

const deletePlaybook = async (id) => {
  if (!confirm('确定要删除这个Playbook吗？')) return
  
  try {
    await api.deletePlaybook(id)
    alert('✅ 删除成功')
    loadData()
  } catch (error) {
    alert('❌ 删除失败: ' + (error.response?.data?.error || error.message))
  }
}

const clearResult = () => {
  currentResult.value = null
}

const copyResult = () => {
  if (!currentResult.value) return
  const text = (currentResult.value.stdout || '') + '\n' + (currentResult.value.stderr || '')
  navigator.clipboard.writeText(text).then(() => {
    alert('✅ 已复制到剪贴板')
  })
}

const downloadResult = () => {
  if (!currentResult.value) return
  const text = (currentResult.value.stdout || '') + '\n' + (currentResult.value.stderr || '')
  const blob = new Blob([text], { type: 'text/plain' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = 'ansible_result.txt'
  a.click()
  URL.revokeObjectURL(url)
}

// 生命周期钩子
onMounted(() => {
  loadData()
  initMonacoEditor()
})

onUnmounted(() => {
  if (editor) {
    editor.dispose()
  }
})
</script>

<style scoped>
@import '../styles/ansible-theme.css';

.action-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding: 18px 24px;
  background: linear-gradient(135deg, #fef5f8 0%, #fff0f6 100%);
  border-radius: 16px;
  border: 2px solid transparent;
  background-image: 
    linear-gradient(#fff, #fff),
    linear-gradient(135deg, rgba(255, 107, 157, 0.3), rgba(196, 113, 237, 0.3));
  background-origin: border-box;
  background-clip: padding-box, border-box;
  box-shadow: 0 2px 12px rgba(255, 107, 157, 0.1);
}

.tabs {
  display: flex;
  gap: 10px;
}

.tab-btn {
  padding: 10px 20px;
  background: var(--color-bg-primary);
  border: 2px solid var(--color-border);
  color: var(--color-text-primary);
  border-radius: 12px;
  cursor: pointer;
  transition: all 0.3s ease;
  font-size: 14px;
  font-weight: 600;
}

.tab-btn:hover {
  border-color: var(--color-accent-pink);
  background: var(--color-bg-tertiary);
  transform: translateY(-2px);
}

.tab-btn.active {
  background: linear-gradient(135deg, #ff6b9d, #c471ed);
  border-color: transparent;
  color: white;
  box-shadow: 0 4px 15px rgba(255, 107, 157, 0.3);
}

.content-container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  height: calc(100vh - 280px);
}

.editor-panel,
.result-panel {
  background: var(--color-bg-secondary);
  border-radius: 16px;
  border: 2px solid transparent;
  background-image: 
    linear-gradient(#fff, #fff),
    linear-gradient(135deg, rgba(255, 107, 157, 0.3), rgba(196, 113, 237, 0.3));
  background-origin: border-box;
  background-clip: padding-box, border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 2px 12px rgba(255, 107, 157, 0.1);
}

.panel-header {
  padding: 18px 20px;
  background: linear-gradient(135deg, #fef5f8 0%, #fff0f6 100%);
  border-bottom: 2px solid transparent;
  border-image: linear-gradient(90deg, #ff6b9d, #c471ed, #12cad6) 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.config-row {
  display: flex;
  align-items: center;
  gap: 15px;
}

.config-row label {
  color: #ff6b9d;
  font-size: 14px;
  font-weight: 600;
  min-width: 100px;
  white-space: nowrap;
}

.select-input {
  flex: 1;
  padding: 8px 12px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 182, 193, 0.3);
  color: #FFB6C1;
}

.breadcrumb-separator {
  color: rgba(255, 255, 255, 0.3);
  font-size: 12px;
}

.path-actions {
  display: flex;
  gap: 8px;
  margin-left: 15px;
}

.remote-path-input {
  flex: 1;
  max-width: 250px;
  padding: 8px 12px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(135, 206, 250, 0.3);
  border-radius: 4px;
  color: #87CEEB;
  font-size: 13px;
}

.remote-path-input:focus {
  outline: none;
  border-color: #87CEEB;
}

.monaco-editor-container {
  flex: 1;
  min-height: 0;
  border: 1px solid rgba(255, 182, 193, 0.2);
  border-radius: 4px;
  overflow: hidden;
}

.code-editor-wrapper {
  flex: 1;
  position: relative;
  overflow: auto;
  background: #0a0e1a;
}

.line-numbers {
  position: absolute;
  left: 0;
  top: 0;
  width: 50px;
  background: rgba(0, 0, 0, 0.3);
  border-right: 1px solid rgba(255, 182, 193, 0.2);
  user-select: none;
  padding: 15px 0;
}

.line-number {
  text-align: right;
  padding-right: 10px;
  color: rgba(255, 255, 255, 0.3);
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 13px;
  line-height: 1.6;
  height: 21px;
}

.code-editor {
  width: 100%;
  height: 100%;
  padding: 15px 15px 15px 60px;
  background: transparent;
  border: none;
  color: #87CEEB;
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 14px;
  line-height: 1.6;
  resize: none;
  outline: none;
  tab-size: 2;
}

.result-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px;
  background: rgba(255, 182, 193, 0.1);
  border-bottom: 1px solid rgba(255, 182, 193, 0.2);
}

.result-title {
  color: #FFB6C1;
  font-size: 16px;
  font-weight: 600;
}

.result-actions {
  display: flex;
  gap: 8px;
}

.result-content {
  flex: 1;
  overflow: auto;
  padding: 20px;
}

.result-empty {
  text-align: center;
  padding: 60px 20px;
  color: rgba(255, 255, 255, 0.5);
}

.empty-icon {
  font-size: 64px;
  margin-bottom: 15px;
}

.status-badge {
  display: inline-block;
  padding: 8px 16px;
  border-radius: 20px;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 15px;
}

.status-running {
  background: rgba(255, 184, 0, 0.2);
  color: #FFB800;
  border: 1px solid #FFB800;
}

.status-success {
  background: rgba(0, 255, 200, 0.2);
  color: #00FFC8;
  border: 1px solid #00FFC8;
}

.status-failed {
  background: rgba(255, 70, 85, 0.2);
  color: #FF4655;
  border: 1px solid #FF4655;
}

.result-stats {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
  padding: 15px;
  background: rgba(0, 0, 0, 0.2);
  border-radius: 6px;
}

.stat-item {
  display: flex;
  gap: 8px;
}

.stat-label {
  color: rgba(255, 255, 255, 0.6);
}

.stat-value {
  color: #FFB6C1;
  font-weight: 600;
}

.output-section {
  margin-bottom: 20px;
}

.output-section h4 {
  color: #FFB6C1;
  margin-bottom: 10px;
  font-size: 14px;
}

.output-text {
  background: #0a0e1a;
  padding: 15px;
  border-radius: 6px;
  border: 1px solid rgba(255, 182, 193, 0.2);
  color: #87CEEB;
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 13px;
  line-height: 1.6;
  overflow-x: auto;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.output-section.error .output-text {
  color: #FF4655;
  border-color: rgba(255, 70, 85, 0.3);
}

/* 按钮样式 */
.btn {
  padding: 8px 16px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  transition: all 0.3s;
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.2);
}

.btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(255, 182, 193, 0.3);
}

.btn-primary {
  background: linear-gradient(135deg, #FFB6C1, #87CEEB);
  border: none;
}

.btn-success {
  background: linear-gradient(135deg, #00FFC8, #4CAF50);
  border: none;
}

.btn-info {
  background: linear-gradient(135deg, #87CEEB, #4D94FF);
  border: none;
}

.btn-danger {
  background: linear-gradient(135deg, #FF4655, #FF6B7A);
  border: none;
}

.btn-upload {
  background: linear-gradient(135deg, #FFD700, #FFA500);
  border: none;
  cursor: pointer;
}

.btn-upload:hover {
  transform: translateY(-2px);
}

.btn-sm {
  padding: 6px 12px;
  font-size: 12px;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

/* 弹窗样式 */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.modal-content {
  background: rgba(30, 40, 55, 0.95);
  border-radius: 12px;
  border: 2px solid rgba(255, 182, 193, 0.3);
  width: 90%;
  max-width: 800px;
  max-height: 80vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px;
  background: rgba(255, 182, 193, 0.1);
  border-bottom: 1px solid rgba(255, 182, 193, 0.2);
}

.modal-header h3 {
  margin: 0;
  color: #FFB6C1;
  font-size: 20px;
}

.btn-close {
  background: none;
  border: none;
  color: #fff;
  font-size: 24px;
  cursor: pointer;
  padding: 0;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  transition: all 0.3s;
}

.btn-close:hover {
  background: rgba(255, 70, 85, 0.2);
}

.modal-body {
  flex: 1;
  overflow: auto;
  padding: 20px;
}

.playbook-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.playbook-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px;
  background: rgba(0, 0, 0, 0.2);
  border: 1px solid rgba(255, 182, 193, 0.2);
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.3s;
}

.playbook-item:hover {
  background: rgba(255, 182, 193, 0.1);
  border-color: rgba(255, 182, 193, 0.4);
  transform: translateX(5px);
}

.playbook-title {
  color: #FFB6C1;
  font-weight: 600;
  margin-bottom: 5px;
}

.playbook-meta {
  color: rgba(255, 255, 255, 0.5);
  font-size: 12px;
}

/* ==================== 文件浏览器样式 ==================== */
.file-browser-modal {
  background: linear-gradient(145deg, 
    rgba(40, 25, 35, 0.98) 0%, 
    rgba(25, 30, 45, 0.98) 50%,
    rgba(30, 20, 40, 0.98) 100%);
  border: 2px solid rgba(255, 182, 193, 0.4);
  border-radius: 16px;
  width: 90%;
  max-width: 900px;
  height: 85vh;
  display: flex;
  flex-direction: column;
  box-shadow: 
    0 20px 60px rgba(255, 105, 180, 0.3),
    0 0 0 1px rgba(255, 182, 193, 0.1) inset;
  animation: slideIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
  overflow: hidden;
}

@keyframes slideIn {
  from {
    transform: translateY(-30px) scale(0.95);
    opacity: 0;
  }
  to {
    transform: translateY(0) scale(1);
    opacity: 1;
  }
}

.file-browser-modal .browser-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24px 28px;
  background: linear-gradient(135deg, 
    rgba(255, 182, 193, 0.15) 0%, 
    rgba(255, 105, 180, 0.08) 100%);
  border-bottom: 2px solid rgba(255, 182, 193, 0.3);
  box-shadow: 0 2px 10px rgba(255, 182, 193, 0.1);
}

.header-content {
  display: flex;
  align-items: center;
  gap: 12px;
}

.header-icon {
  font-size: 28px;
  filter: drop-shadow(0 2px 4px rgba(255, 182, 193, 0.3));
}

.browser-header h3 {
  margin: 0;
  background: linear-gradient(135deg, #FFB6C1 0%, #FF69B4 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: 0.5px;
}

.close-btn {
  background: linear-gradient(135deg, rgba(255, 70, 85, 0.2) 0%, rgba(200, 50, 70, 0.2) 100%);
  border: 2px solid rgba(255, 70, 85, 0.4);
  color: #FF4655;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  cursor: pointer;
  font-size: 20px;
  font-weight: bold;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  display: flex;
  align-items: center;
  justify-content: center;
}

.close-btn:hover {
  background: linear-gradient(135deg, rgba(255, 70, 85, 0.4) 0%, rgba(200, 50, 70, 0.4) 100%);
  border-color: #FF4655;
  transform: rotate(90deg) scale(1.1);
  box-shadow: 0 4px 12px rgba(255, 70, 85, 0.4);
}

.browser-path-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 28px;
  background: rgba(0, 0, 0, 0.3);
  border-bottom: 1px solid rgba(255, 182, 193, 0.15);
  gap: 16px;
}

.path-display {
  flex: 1;
  min-width: 0;
}

.path-label {
  display: block;
  font-size: 11px;
  color: rgba(255, 182, 193, 0.6);
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 8px;
  font-weight: 600;
}

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  overflow-x: auto;
  padding: 10px 14px;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(135, 206, 250, 0.3);
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2) inset;
}

.breadcrumb::-webkit-scrollbar {
  height: 6px;
}

.breadcrumb::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.3);
  border-radius: 3px;
}

.breadcrumb::-webkit-scrollbar-thumb {
  background: linear-gradient(90deg, rgba(135, 206, 250, 0.4), rgba(255, 182, 193, 0.4));
  border-radius: 3px;
}

.breadcrumb-item {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #87CEEB;
  font-size: 14px;
  cursor: pointer;
  padding: 6px 12px;
  border-radius: 6px;
  transition: all 0.3s;
  white-space: nowrap;
  font-family: 'Consolas', 'Monaco', monospace;
  background: rgba(135, 206, 250, 0.05);
  border: 1px solid transparent;
}

.breadcrumb-item.root {
  background: rgba(255, 182, 193, 0.1);
  color: #FFB6C1;
  font-weight: 600;
}

.breadcrumb-item:hover {
  background: linear-gradient(135deg, rgba(135, 206, 250, 0.25), rgba(255, 182, 193, 0.15));
  border-color: rgba(255, 182, 193, 0.3);
  color: #FFB6C1;
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(255, 182, 193, 0.2);
}

.breadcrumb-separator {
  color: rgba(255, 182, 193, 0.4);
  font-size: 16px;
  font-weight: bold;
}

.path-actions {
  display: flex;
  gap: 10px;
}

.action-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  background: linear-gradient(135deg, rgba(255, 182, 193, 0.15), rgba(135, 206, 250, 0.1));
  border: 1.5px solid rgba(255, 182, 193, 0.3);
  border-radius: 8px;
  color: #FFB6C1;
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  white-space: nowrap;
}

.action-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, rgba(255, 182, 193, 0.3), rgba(135, 206, 250, 0.2));
  border-color: #FFB6C1;
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(255, 182, 193, 0.3);
}

.action-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  transform: none;
}

.action-btn .icon {
  font-size: 16px;
}

.browser-content {
  flex: 1;
  overflow-y: auto;
  padding: 24px 28px;
  background: rgba(0, 0, 0, 0.2);
}

.browser-content::-webkit-scrollbar {
  width: 10px;
}

.browser-content::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.3);
  border-radius: 5px;
}

.browser-content::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, rgba(255, 182, 193, 0.4), rgba(135, 206, 250, 0.4));
  border-radius: 5px;
}

.browser-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  gap: 20px;
}

.loading-spinner {
  font-size: 64px;
  animation: spin 2s linear infinite;
  filter: drop-shadow(0 4px 8px rgba(255, 182, 193, 0.3));
}

.browser-loading p {
  color: rgba(255, 182, 193, 0.8);
  font-size: 16px;
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.browser-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  gap: 20px;
}

.error-icon {
  font-size: 64px;
  filter: drop-shadow(0 4px 8px rgba(255, 70, 85, 0.3));
}

.error-message {
  color: #FF4655;
  font-size: 16px;
  text-align: center;
}

.retry-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 24px;
  background: linear-gradient(135deg, rgba(255, 182, 193, 0.2), rgba(135, 206, 250, 0.15));
  border: 2px solid rgba(255, 182, 193, 0.4);
  border-radius: 8px;
  color: #FFB6C1;
  cursor: pointer;
  font-size: 14px;
  font-weight: 600;
  transition: all 0.3s;
}

.retry-btn:hover {
  background: linear-gradient(135deg, rgba(255, 182, 193, 0.3), rgba(135, 206, 250, 0.25));
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(255, 182, 193, 0.3);
}

.item-section {
  margin-bottom: 32px;
}

.section-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
  padding: 12px 16px;
  background: linear-gradient(135deg, rgba(255, 182, 193, 0.15), rgba(135, 206, 250, 0.08));
  border-left: 4px solid #FFB6C1;
  border-radius: 8px;
}

.section-icon {
  font-size: 22px;
}

.section-title {
  color: #FFB6C1;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.5px;
}

.section-count {
  margin-left: auto;
  padding: 4px 12px;
  background: rgba(255, 182, 193, 0.2);
  border: 1px solid rgba(255, 182, 193, 0.3);
  border-radius: 12px;
  color: #FFB6C1;
  font-size: 12px;
  font-weight: 600;
}

.items-grid {
  display: grid;
  gap: 12px;
}

.browser-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
  border: 1.5px solid rgba(255, 255, 255, 0.1);
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  position: relative;
  overflow: hidden;
}

.browser-item::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 4px;
  height: 100%;
  background: linear-gradient(180deg, transparent, #FFB6C1, transparent);
  opacity: 0;
  transition: opacity 0.3s;
}

.browser-item:hover::before {
  opacity: 1;
}

.browser-item:hover {
  background: linear-gradient(135deg, rgba(255, 182, 193, 0.2), rgba(135, 206, 250, 0.1));
  border-color: rgba(255, 182, 193, 0.4);
  transform: translateX(8px) scale(1.02);
  box-shadow: 0 6px 20px rgba(255, 182, 193, 0.25);
}

.item-icon-wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  background: rgba(255, 182, 193, 0.1);
  border-radius: 10px;
  transition: all 0.3s;
}

.browser-item:hover .item-icon-wrapper {
  background: rgba(255, 182, 193, 0.2);
  transform: scale(1.1);
}

.directory-item .item-icon {
  font-size: 28px;
  filter: drop-shadow(0 2px 4px rgba(135, 206, 250, 0.3));
}

.file-item .item-icon {
  font-size: 26px;
  filter: drop-shadow(0 2px 4px rgba(255, 182, 193, 0.3));
}

.item-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.item-name {
  color: #fff;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.3px;
}

.item-size {
  font-size: 12px;
  color: rgba(255, 182, 193, 0.6);
  font-family: 'Consolas', 'Monaco', monospace;
}

.item-arrow {
  font-size: 24px;
  color: rgba(255, 182, 193, 0.4);
  transition: all 0.3s;
}

.browser-item:hover .item-arrow {
  color: #FFB6C1;
  transform: translateX(4px);
}

.browser-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80px 20px;
  gap: 16px;
}

.empty-icon {
  font-size: 80px;
  filter: drop-shadow(0 4px 8px rgba(255, 182, 193, 0.2));
  opacity: 0.5;
}

.empty-title {
  color: rgba(255, 182, 193, 0.8);
  font-size: 18px;
  font-weight: 600;
  margin: 0;
}

.empty-subtitle {
  color: rgba(255, 255, 255, 0.4);
  font-size: 14px;
  margin: 0;
}
</style>
